
<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta name="description" content="授权证书预览 - 若通音乐（Ruotong Music）。页面：license-certificate。高品质商用音乐与音效，授权便捷，企业合作支持。"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>授权证书预览</title>

<style>body{background:#f5f7fb}.paper{max-width:880px;margin:24px auto;background:#fff;color:#111;padding:28px;border-radius:12px;box-shadow:0 10px 30px rgba(16,24,40,.1)}h1{margin:0 0 8px;font-size:22px}.grid{display:grid;grid-template-columns:1.2fr .8fr;gap:12px}.kv{display:grid;grid-template-columns:120px 1fr;gap:6px 12px}.kv .key{color:#6b7280;text-align:right}.group{border:1px dashed #e5e7eb;border-radius:8px;padding:12px}.foot{display:flex;justify-content:space-between;margin-top:14px}.muted{color:#6b7280}.qr{width:128px;height:128px;border:1px solid #e5e7eb}@media print{body{background:#fff}.paper{box-shadow:none;margin:0;border-radius:0}}</style>

<!-- SEO 多语言模板：将 href 替换为实际三语路径 -->
<link rel="alternate" hreflang="zh" href="/zh/" />
<link rel="alternate" hreflang="en" href="/en/" />
<link rel="alternate" hreflang="ja" href="/ja/" />

<!-- JSON-LD 占位：为满足 CSP（script-src 'self'），请改为带 nonce/hash 的内联脚本，或由服务端注入。 -->

<meta name="theme-color" content="#000000" />
<!-- 占位：请替换为页面真实描述 --><!-- 占位：社交分享基础字段 -->
<meta property="og:type" content="website" />
<meta property="og:title" content="若通音乐素材平台" />
<meta property="og:description" content="高质量配乐与音效素材，支持试听、购买、下载授权。" />
<meta property="og:image" content="https://example.com/og-image.png" />

<!-- 占位：请替换为实际页面地址 --><link rel="canonical" href="https://example.com/" />





<link rel="stylesheet" href="../assets/css/license-certificate.min.css">
</head><body>
<style>
.skip-to-content{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden;}
.skip-to-content:focus{left:16px;top:16px;width:auto;height:auto;z-index:10000;background:#fff;padding:8px 12px;border:1px solid #000;}
:focus-visible{outline:2px solid currentColor;outline-offset:2px;}
</style>
<a class="skip-to-content" href="#main">跳转到主内容</a>


<div class="paper" id="paper"><header><h1>音乐使用授权证书</h1><div class="muted" id="meta">证书编号：<span id="certId">—</span> · 签发日期：<span id="date">—</span></div></header>
<div class="grid" style="margin-top:18px"><section class="group"><h3>授权信息</h3><div class="kv" id="kv1"></div></section><aside class="group"><h3>验真二维码</h3><canvas id="qr" class="qr"></canvas><div class="muted small">扫码验真：仅用于线下对账与内部存档</div></aside></div>
<section class="group" style="margin-top:12px"><h3>授权范围</h3><ul id="scope"></ul></section>
<div class="foot"><div><strong>授权方：</strong>若通音乐 Ruotong Music<br><strong>联系人：</strong>support@ruotongmusic.com</div><div><strong>被授权方：</strong><span id="licensee">—</span><br><strong>项目：</strong><span id="project">—</span></div></div></div>
<script>(function(global){function qrmatrix(data){const gfExp=new Array(512),gfLog=new Array(256);let x=1;for(let i=0;i<255;i++){gfExp[i]=x;gfLog[x]=i;x<<=1;if(x&0x100)x^=0x11d}for(let i=255;i<512;i++)gfExp[i]=gfExp[i-255];function mul(a,b){if(a===0||b===0)return 0;return gfExp[(gfLog[a]+gfLog[b])%255]}const bytes=Array.from(new TextEncoder().encode(data));if(bytes.length>17)throw new Error('data too long for v1-L');let bits=[];function pushBits(v,l){for(let i=l-1;i>=0;i--)bits.push((v>>i)&1)}pushBits(4,4);pushBits(bytes.length,8);bytes.forEach(b=>pushBits(b,8));const maxDataBits=152;const terminator=Math.min(4,maxDataBits-bits.length);for(let i=0;i<terminator;i++)bits.push(0);while(bits.length%8!==0)bits.push(0);const pad=[0xEC,0x11];let dataCw=[];for(let i=0;i<bits.length;i+=8)dataCw.push(parseInt(bits.slice(i,i+8).join(''),2));while(dataCw.length<19)dataCw.push(pad[dataCw.length%2]);let gen=[1];for(let i=0;i<7;i++){const g=[];for(let j=0;j<gen.length;j++)g[j]=mul(gen[j],gfExp[i]);gen=[...gen.map(c=>mul(c,1)),0];for(let j=0;j<gen.length-1;j++)gen[j]^=g[j]||0}let rem=new Array(7).fill(0);for(let i=0;i<dataCw.length;i++){const factor=dataCw[i]^rem[0];rem.shift();rem.push(0);for(let j=0;j<7;j++){rem[j]^=mul(gen[j],factor)}}const ec=rem;const codewords=dataCw.concat(ec);const n=21;const m=Array.from({length:n},()=>Array(n).fill(null));function placeFinder(r,c){for(let i=-1;i<=7;i++)for(let j=-1;j<=7;j++){const rr=r+i,cc=c+j;if(rr<0||rr>=n||cc<0||cc>=n)continue;const on=(i>=0&&i<=6&&j>=0&&j<=6)&&(i===0||i===6||j===0||j===6||(i>=2&&i<=4&&j>=2&&j<=4));m[rr][cc]=on?1:0}}placeFinder(0,0);placeFinder(0,n-7);placeFinder(n-7,0);for(let i=8;i<n-8;i++){m[6][i]=(i%2===0)?1:0;m[i][6]=(i%2===0)?1:0}for(let i=0;i<9;i++){if(m[8][i]===null)m[8][i]=0;if(m[i][8]===null)m[i][8]=0}for(let i=n-8;i<n;i++){if(m[8][i]===null)m[8][i]=0;if(m[i][8]===null)m[i][8]=0}m[n-8][8]=1;let dirUp=true,col=n-1,bitIdx=0;function nextPos(){while(true){if(col===6)col--;for(let i=0;i<n;i++){const r=dirUp?(n-1-i):i;for(let k=0;k<2;k++){const c=col-k;if(m[r][c]===null){return{r,c}}}}col-=2;dirUp=!dirUp;if(col<0)return null}}const allBits=codewords.map(b=>b.toString(2).padStart(8,'0')).join('').split('').map(Number);while(true){const pos=nextPos();if(!pos)break;m[pos.r][pos.c]=allBits[bitIdx++]||0}for(let r=0;r<n;r++)for(let c=0;c<n;c++){const inFunc=(function(){if(r<9&&c<9)return true;if(r<9&&c>=n-8)return true;if(r>=n-8&&c<9)return true;if(r===6||c===6)return true;return false})();if(!inFunc){m[r][c]^=((r+c)%2===0)?1:0}}const fmt=0b111011111000100;for(let i=0;i<6;i++){m[8][i]=(fmt>>i)&1}m[8][7]=(fmt>>6)&1;m[8][8]=(fmt>>7)&1;m[7][8]=(fmt>>8)&1;for(let i=9;i<15;i++){m[14-i][8]=(fmt>>i)&1}for(let i=0;i<8;i++){m[n-1-i][8]=(fmt>>i)&1}for(let i=0;i<7;i++){m[8][n-1-i]=(fmt>>i)&1}return m}function drawQR(canvas,text){const mat=qrmatrix(text);const n=mat.length,scale=Math.floor(Math.min(canvas.width,canvas.height)/(n+8));const ctx=canvas.getContext('2d');ctx.fillStyle='#fff';ctx.fillRect(0,0,canvas.width,canvas.height);const off=Math.floor((canvas.width-n*scale)/2);ctx.fillStyle='#000';for(let r=0;r<n;r++)for(let c=0;c<n;c++){if(mat[r][c])ctx.fillRect(off+c*scale,off+r*scale,scale,scale)}}global.drawQR=drawQR})(window);
function qs(k){return new URLSearchParams(location.search).get(k)}; const trackId=qs('trackId'); const licenses=JSON.parse(localStorage.getItem('rtm.licenses')||'{}'); const data=licenses[trackId];
if(!data){ document.getElementById('paper').innerHTML='<p>未找到证书数据。请先在账号中心→授权证书中填写授权范围后再试。</p>'; }else{
  const certId='LIC-'+trackId; document.getElementById('certId').textContent=certId; document.getElementById('date').textContent=(data.updatedAt||'').slice(0,10);
  function addKV(k,v){ const kv1=document.getElementById('kv1'); kv1.insertAdjacentHTML('beforeend','<div class="key">'+k+'</div><div>'+v+'</div>'); }
  addKV('作品名称', data.title); addKV('艺人/作者', data.artist); addKV('订单编号', data.orderId); addKV('购买规格', data.spec); addKV('授权地区', data.region); addKV('使用期限', data.term); addKV('投放类型', data.ads + (data.budget ? ('（预算 '+data.budget+' 万）') : ''));
  document.getElementById('licensee').textContent=(data.licensee||'—'); document.getElementById('project').textContent=data.project||'—';
  const scope=document.getElementById('scope'); const li1=document.createElement('li'); li1.textContent='媒体类型：'+(data.media||[]).join('、'); scope.appendChild(li1); const li2=document.createElement('li'); li2.textContent='发布平台：'+(data.platform||[]).join('、'); scope.appendChild(li2); if(data.notes){ const n=document.createElement('li'); n.textContent='备注：'+data.notes; scope.appendChild(n); }
  const canvas=document.getElementById('qr'); drawQR(canvas, certId);
}
if(qs('print')==='1'){ window.onload=()=>setTimeout(()=>window.print(),300); }
</script>
<!-- Ruotong Account Buttons (auto-wired) -->
<script>window.RUOTONG_API_BASE = window.RUOTONG_API_BASE || '/api';</script>
<link rel="stylesheet" href="/rt-assets/css/toast.css">
<link rel="stylesheet" href="/rt-assets/css/modal.css">
<script src="/rt-assets/js/toast.js" defer></script>
<script src="/rt-assets/js/modal.js" defer></script>
<script src="/rt-assets/js/account-actions.js" defer></script>


<script>
document.addEventListener('DOMContentLoaded', function(){
  try{ document.body.classList.add('rt-darkfix'); }catch(e){}
});
</script>

</body></html>
